using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace net.notdot.Attercop.Client
{    
    /// <summary>
    /// Constructs a tree of PathNodes from a filesystem
    /// </summary>
    public class TreeBuilder
    {
        public class HashTask : ITask
        {
            public readonly FileInfo info;
            private Dictionary<string, HashCacheEntry> cache;
            private PathNode node;
            private Stream s;

            public HashTask(FileInfo info, PathNode node, Dictionary<string, HashCacheEntry> cache)
            {
                this.info = info;
                this.node = node;
                this.cache = cache;
            }

            #region ITask Members

            public void DoTask()
            {
                //Calculate the hash
                SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
                s = info.OpenRead();
                byte[] hash = sha1.ComputeHash(s);

                //Add it to the cache
                HashCacheEntry entry = new HashCacheEntry();
                entry.Hash = hash;
                entry.LastModified = info.LastWriteTimeUtc;
                if (cache.ContainsKey(info.FullName))
                {
                    cache[info.FullName] = entry;
                }
                else
                {
                    cache.Add(info.FullName, entry);
                }

                //Update the PathNode
                node.Hash = hash;

                s = null;

                System.Diagnostics.Debug.WriteLine(info.FullName);
            }

            public double TaskSize
            {
                get
                {
                    return info.Length;
                }
            }

            public double TaskComplete
            {
                get
                {
                    if (s != null)
                        return s.Position;
                    return 0;
                }
            }

            public string TaskCategory
            {
                get
                {
                    return Directory.GetDirectoryRoot(info.FullName);
                }
            }

            #endregion
        }

        public readonly Dictionary<string, HashCacheEntry> HashCache;
        public readonly TaskThreadCollection tasks;

        public FileListingIndex Index = new FileListingIndex();

        public TreeBuilder(Dictionary<string,HashCacheEntry> hashCache, TaskThreadCollection tasks)
        {
            this.HashCache = hashCache;
            this.tasks = tasks;
        }

        public PathNode BuildTree(string path)
        {
            string name = Path.GetFileName(path);
            if (name == "")
                name = Path.GetDirectoryName(path);
            if (name == null)
                name = Path.GetPathRoot(path);
            PathNode ret = new PathNode(name, Directory.Exists(path));

            if (!ret.IsDir)
            {
                //File
                FileInfo info = new FileInfo(path);
                if (HashCache.ContainsKey(path) && HashCache[path].LastModified == info.LastWriteTimeUtc)
                {
                    ret.Hash = HashCache[path].Hash;
                }
                else
                {
                    //Add the node to the TaskThreadCollection
                    HashTask task = new HashTask(info, ret, HashCache);
                    tasks.AddTask(task);
                }

                ret.TotalSize = info.Length;
            }

            //Add the node to the index
            Index.AddNode(ret, false);

            //Add all the child nodes
            if (ret.IsDir)
            {
                try
                {
                    string[] entries = Directory.GetFileSystemEntries(path);
                    Array.Sort(entries);
                    for (int i = 0; i < entries.Length; i++)
                    {
                        PathNode child = BuildTree(entries[i]);
                        ret.TotalSize += child.TotalSize;
                        ret.Children.Add(child);
                    }                        
                }
                catch (UnauthorizedAccessException) { }
            }

            return ret;
        }
    }
}
